package com.zmsc.crypto;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Crypto
  {
   private final static int ITERATION_NUMBER = 1000;
 
   public Crypto()
     {
     }
   
   /**
    * Inserts a new user in the database
    * @param con Connection An open connection to a databse
    * @param login String The login of the user
    * @param password String The password of the user
    * @return boolean Returns true if the login and password are ok (not null and length(login)<=100
    * @throws SQLException If the database is unavailable
    * @throws NoSuchAlgorithmException If the algorithm SHA-1 or the SecureRandom is not supported by the JVM
    */
   public boolean createUser (Connection con, String login, String password) throws SQLException, NoSuchAlgorithmException
     {
      PreparedStatement ps = null;
      try
        {
         if ((login != null) && (password != null) && (login.length() <= 100))
           {
            // Uses a secure Random not a simple Random
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            // Salt generation 64 bits long
            byte[] bSalt = new byte[8];
            random.nextBytes(bSalt);
            // Digest computation
            byte[] bDigest = getHash(ITERATION_NUMBER, password, bSalt);
            String sDigest = byteToBase64(bDigest);
            String sSalt   = byteToBase64(bSalt);
            ps             = con.prepareStatement("INSERT INTO CREDENTIAL (LOGIN, PASSWORD, SALT) VALUES (?,?,?)");
            ps.setString(1,login);
            ps.setString(2,sDigest);
            ps.setString(3,sSalt);
            ps.executeUpdate();
            return true;
           }
         else
           {
            return false;
           }
        }
      finally
        {
         close(ps);
        }
     }
   
   /**
    * From a password, a number of iterations and a salt,
    * returns the corresponding digest
    * @param iterationNb int The number of iterations of the algorithm
    * @param password String The password to encrypt
    * @param salt byte[] The salt
    * @return byte[] The digested password
    * @throws NoSuchAlgorithmException If the algorithm doesn't exist
    */
   public byte[] getHash (int iterationNb, String password, byte[] salt) throws NoSuchAlgorithmException
     {
      MessageDigest digest = MessageDigest.getInstance("SHA-1");
      digest.reset();
      digest.update(salt);
      byte[]        input  = digest.digest(password.getBytes("UTF-8"));
      for (int i = 0; i < iterationNb; i++)
        {
         digest.reset();
         input = digest.digest(input);
        }
      return input;
     }
   
   public void creerTable (Connection con) throws SQLException
     {
	   PreparedStatement st = null;
      try
        {
         st = con.prepareStatement("CREATE TABLE CREDENTIAL (LOGIN VARCHAR(100) PRIMARY KEY, PASSWORD VARCHAR(32) NOT NULL, SALT VARCHAR(32) NOT NULL)");
         st.executeUpdate();
        }
      finally
        {
         close(st);
        }
     }
 
   /**
    * Closes the current statement
    * @param ps Statement
    */
   public void close (PreparedStatement	 ps)
     {
      if (ps != null)
        {
         try
           {
            ps.close();
           }
         catch (SQLException ignore)
           {
           }
        }
     }
 
   /**
    * Closes the current resultset
    * @param rs ResultSet
    */
   public void close (ResultSet rs)
     {
      if (rs != null)
        {
         try
           {
            rs.close();
           }
         catch (SQLException ignore)
           {
           }
        }
     }
   
   /**
    * From a base 64 representation, returns the corresponding byte[] 
    * @param data String The base64 representation
    * @return byte[]
    * @throws IOException
    */
   public static byte[] base64ToByte (String data) throws IOException
     {
      BASE64Decoder decoder = new BASE64Decoder();
      return decoder.decodeBuffer(data);
     }
 
   /**
    * From a byte[] returns a base 64 representation
    * @param data byte[]
    * @return String
    * @throws IOException
    */
   public static String byteToBase64 (byte[] data)
     {
      BASE64Encoder endecoder = new BASE64Encoder();
      return endecoder.encode(data);
     }
  }

